import json
import logging
from decimal import Decimal

from django.contrib.auth.mixins import LoginRequiredMixin
from django.db import transaction
from django.http.response import JsonResponse
from django.shortcuts import render
from django.utils import timezone
from django.views import View
from django_redis import get_redis_connection
from utils.response_code import RETCODE
from utils.views import LoginRequiredJSONMixin

from apps.goods.models import SKU
from apps.orders.models import OrderGoods, OrderInfo
from apps.users.models import Address

# Create your views here.


logger = logging.getLogger('django')


class OrderSettlementView(LoginRequiredMixin, View):
    """生成订单页面"""

    def get(self, request):
        """提供订单结算页面"""
        # 获取登录用户
        user = request.user
        # 查询地址信息
        try:
            addresses = Address.objects.filter(
                user=user, is_deleted=False)
        except Address.DoesNotExist as exc:
            # 如果地址为空
            logger.error(exc)
            return render(request, 'place_order.html',
                          context={'errmsg': '地址查询失败'})

        # 从Redis购物车中查询出被勾选的商品信息
        redis_conn = get_redis_connection('carts')
        redis_cart = redis_conn.hgetall('carts_%s' % user.id)
        cart_selected = redis_conn.smembers('selected_%s' % user.id)

        cart = {}
        for sku_id in cart_selected:
            cart[int(sku_id)] = int(redis_cart[sku_id])

        # 准备初始值
        total_count = 0
        total_amount = Decimal(0.00)  # 与金钱有关的数据须使用decimal类型
        # 查询商品信息
        skus = SKU.objects.filter(id__in=cart.keys())
        for sku in skus:
            sku.count = cart[sku.id]
            sku.amount = sku.count * sku.price
            # 计算总数量和总金额
            total_count += sku.count
            total_amount += sku.count * sku.price
        # 补充运费
        freight = Decimal('10.00')

        # 渲染界面
        context = {
            'addresses': addresses,
            'skus': skus,
            'total_count': total_count,
            'total_amount': total_amount,
            'freight': freight,
            'payment_amount': total_amount + freight
        }

        return render(request, 'place_order.html', context)


class OrderView(LoginRequiredJSONMixin, View):
    """提交订单"""

    def post(self, request):
        """提交订单"""
        user = request.user
        # 获取提交的信息
        data = json.loads(request.body.decode())
        address_id = data.get('address_id')
        pay_method = data.get('pay_method')

        if not all(address_id, pay_method):
            # 数据不全，返回错误信息
            return JsonResponse({'code': RETCODE.PARAMERR, 'errmsg': '参数不全'})

        # 获取地址信息
        try:
            address = Address.objects.get(address_id)
        except Address.DoesNotExist:
            return JsonResponse({'code': RETCODE.NODATAERR, 'errmsg': '地址不存在'})

        # 获取支付方式
        # 判断支付方式是否是指定的支付宝或货到付款方式
        if pay_method not in [OrderInfo.PAY_METHODS_ENUM['ALIPAY'],
                              OrderInfo.PAY_METHODS_ENUM['CASH']]:
            return JsonResponse({'code': RETCODE.PARAMERR, 'errmsg': '支付方式错误'})

        # 生成订单号 order_id = 年月日时分秒 + 9位用户ID
        generate_date = timezone.localtime().strftime('%Y%m%d%H%M%S')
        order_id = generate_date + '%09d' % user.id

        # 初始化总价、总数量、运费
        total_amount = Decimal(0.00)
        total_count = 0
        freight = Decimal(12.00)

        # 判断支付方式，设置订单状态
        if pay_method == OrderInfo.PAY_METHODS_ENUM['CASH']:  # 货到付款
            order_status = OrderInfo.ORDER_STATUS_ENUM['UNSEND']  # 待发货
        else:
            pay_method == OrderInfo.PAY_METHODS_ENUM['ALIPAY']  # 支付宝付款
            order_status = OrderInfo.ORDER_STATUS_ENUM['UNPAID']  # 待支付

        # 开启事务的原子性操作
        with transaction.atomic():
            # 设置回滚点
            rollback_point = transaction.savepoint()

            # 生成订单信息并保存在数据库中
            order = OrderInfo.objects.create(
                order_id=order_id,
                user=user,
                address=address,
                total_count=total_count,
                total_amount=total_amount,
                pay_method=pay_method,
                freight=freight,
                status=order_status
            )

            # 获取选中商品的信息和数量
            conn = get_redis_connection('carts')
            pipe = conn.pipeline()
            pipe.hgetall('carts_%s' % user.id)
            pipe.smembers('selected_%s' % user.id)
            goods_id_count, selected_ids = pipe.execute()

            # 整理商品ID和数量：{商品ID:数量}
            selected_carts = {}
            for id in selected_ids:
                selected_carts[int(id)] = int(goods_id_count[id])

            ids = selected_carts.keys()
            for id in ids:
                # 查询一件商品的SKU信息
                sku = SKU.objects.get(id=id)
                # 判断库存是否足够
                count = selected_carts[sku.id]
                old_stock = sku.stock
                if old_stock < count:
                    # 回滚到指定回滚点，撤销生成的订单信息
                    transaction.savepoint_rollback(rollback_point)
                    return JsonResponse({'code': RETCODE.STOCKERR,
                                        'errmsg': '库存不足'})

                # 使用乐观锁减少库存，增加销量（需要配合MySQL的事务隔离级别：Read committed）
                new_stock = sku.stock - count
                new_sales = sku.sales + count

                # 更新时，需要保证库存与之前查询出的数据一致
                result = SKU.objects.filter(id=sku.id, stock=old_stock).update(
                    stock=new_stock, sales=new_sales)

                if result == 0:
                    # 下单失败，进行回滚
                    transaction.savepoint_rollback(rollback_point)
                    return JsonResponse({'code': RETCODE.STOCKERR,
                                         'errmsg': '下单失败'})

                # 保存订单中商品的信息
                OrderGoods.objects.create(
                    order=order,
                    sku=sku,
                    count=count,
                    price=sku.price
                )
                # 计算总数量和总金额
                order.total_count += count
                order.total_amount += (total_count * sku.price)
            order.save()

            # 提交事务
            transaction.savepoint_commit(rollback_point)

        # 清除购物车（redis）中成功结算的商品
        pipe.hdel('carts_%s' % user.id, *selected_ids)
        pipe.srem('selected_%s' % user.id, *selected_ids)
        pipe.execute()

        return JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK',
                             'order_id': order_id})


class OrderSuccess(LoginRequiredMixin, View):
    """展示订单提交成功页面"""

    def get(self, request):
        """返回订单提交成功页面"""
        order_id = request.GET.get('order_id')
        payment_amount = request.GET.get('payment_amount')
        pay_method = request.GET.get('pay_method')

        context = {
            'order_id': order_id,
            'payment_amount': payment_amount,
            'pay_method': pay_method
        }
        return render(request, 'order_success.html', context)

